package com.apobates.jforum2.troll.accounts.biz.impl.dao;

import com.apobates.jforum2.troll.accounts.biz.dao.MemberActiveRecordsDao;
import com.apobates.jforum2.troll.accounts.entity.Member;
import com.apobates.jforum2.troll.accounts.entity.MemberActiveRecords;
import com.apobates.jforum2.troll.regular.ForumActionEnum;
import com.apobates.jforum2.troll.utils.persistence.Page;
import com.apobates.jforum2.troll.utils.persistence.Pageable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 *
 * @author xiaofanku
 * @since 20200515
 */
@Repository
public class MemberActiveRecordsDaoImpl implements MemberActiveRecordsDao{
    @PersistenceContext
    private EntityManager entityManager;
    private final static Logger logger = LoggerFactory.getLogger(MemberActiveRecordsDaoImpl.class);
    
    @Override
    public Page<MemberActiveRecords> findAll(LocalDateTime start, LocalDateTime finish, Pageable pageable) {
        final long total = findAllCount(start, finish);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<MemberActiveRecords> query = entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar WHERE mar.activeDateTime BETWEEN ?1 AND ?2 ORDER BY mar.activeDateTime DESC", MemberActiveRecords.class)
                .setParameter(1, start)
                .setParameter(2, finish);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<MemberActiveRecords> result = query.getResultStream();
        return new Page<MemberActiveRecords>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<MemberActiveRecords> getResult() {
                return result;
            }
        };
    }
    
    private long findAllCount(LocalDateTime start, LocalDateTime finish) {
        try {
            return entityManager.createQuery("SELECT COUNT(mar) FROM MemberActiveRecords mar WHERE mar.activeDateTime BETWEEN ?1 AND ?2", Long.class)
                    .setParameter(1, start)
                    .setParameter(2, finish)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[findAllCount][MemberActiveRecordsDao]", e);
            }
        }
        return 0L;
    }
    @Override
    public Page<MemberActiveRecords> findAllByMember(long memberId, Pageable pageable) {
        final long total = countByMember(memberId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<MemberActiveRecords> query = entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar WHERE mar.memberId = ?1 ORDER BY mar.activeDateTime DESC", MemberActiveRecords.class).setParameter(1, memberId);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<MemberActiveRecords> result = query.getResultStream();
        return new Page<MemberActiveRecords>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<MemberActiveRecords> getResult() {
                return result;
            }
        };
    }
    
    private long countByMember(long memberId) {
        try {
            return entityManager.createQuery("SELECT COUNT(mar) FROM MemberActiveRecords mar WHERE mar.memberId = ?1", Long.class)
                    .setParameter(1, memberId)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[countByMember][MemberActiveRecordsDao]", e);
            }
        }
        return 0L;
    }
    
    @Override
    public Stream<MemberActiveRecords> findAllByMember(long memberId, String memberNames, int showSize) {
        return entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar WHERE mar.memberNames = ?1 AND mar.action = ?2 ORDER BY mar.id DESC", MemberActiveRecords.class)
                .setParameter(1, memberNames)
                .setParameter(2, ForumActionEnum.MEMBER_LOGIN)
                .setMaxResults(showSize)
                .getResultStream();
    }
    
    @Override
    public Stream<MemberActiveRecords> findAllByMemberNames(String memberNames, int showSize) {
        return entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar WHERE mar.memberNames = ?1 AND "
                + "mar.action = ?2 AND "
                + "mar.succeed = ?3 AND "
                + "mar.city IS NOT NULL AND "
                + "mar.province IS NOT NULL "
                + "ORDER BY mar.id DESC", MemberActiveRecords.class)
                .setParameter(1, memberNames)
                .setParameter(2, ForumActionEnum.MEMBER_LOGIN)
                .setParameter(3, true)
                .setMaxResults(showSize)
                .getResultStream();
    }
    
    @Override
    public Stream<MemberActiveRecords> findAllLoginAction(long memberId, String memberNames, int showSize) {
        return entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar WHERE mar.memberNames = ?1 AND mar.action = ?2 AND mar.succeed = ?3 ORDER BY mar.id DESC", MemberActiveRecords.class)
                .setParameter(1, memberNames)
                .setParameter(2, ForumActionEnum.MEMBER_LOGIN)
                .setParameter(3, true)
                .setMaxResults(showSize)
                .getResultStream();
    }
    
    @Override
    public Map<ForumActionEnum, Long> statsMemberAction(long memberId) {
        Member m = entityManager.find(Member.class, memberId);
        if (null == m) {
            return Collections.emptyMap();
        }
        // 就是统计登录次数
        Long count = 0L;
        try {
            count = entityManager.createQuery("SELECT COUNT(mar) FROM MemberActiveRecords mar WHERE mar.memberNames = ?1 AND mar.succeed = ?2 AND mar.action = ?3", Long.class)
                    .setParameter(1, m.getNames())
                    .setParameter(2, true)
                    .setParameter(3, ForumActionEnum.MEMBER_LOGIN)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[statsMemberTopicAction][MemberActiveRecordsDao]", e);
            }
        }
        Map<ForumActionEnum, Long> data = new EnumMap<>(ForumActionEnum.class);
        data.put(ForumActionEnum.MEMBER_REGISTER, 1L);
        data.put(ForumActionEnum.MEMBER_LOGIN, count);
        return data;
    }
    
    @Override
    public Map<ForumActionEnum, Long> statsMemberAction(long memberId, List<ForumActionEnum> actions) {
        if (null == actions || actions.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<ForumActionEnum, Long> data = statsMemberAction(memberId);
        List<ForumActionEnum> params = new ArrayList<>();
        for (ForumActionEnum fae : actions) {
            if (ForumActionEnum.MEMBER_REGISTER != fae && ForumActionEnum.MEMBER_REGISTER != fae) {
                params.add(fae);
            }
        }
        if (params.isEmpty()) {
            return data;
        }
        @SuppressWarnings("unchecked")
        List<Object[]> result = entityManager.createQuery("SELECT mar.action, COUNT(mar) FROM MemberActiveRecords mar WHERE mar.memberId = ?1 AND mar.succeed = ?2 AND mar.action IN ?3 GROUP BY mar.action")
                .setParameter(1, memberId)
                .setParameter(2, true)
                .setParameter(3, params)
                .getResultList();
        data.putAll(groupMemberActionResultMapping(result));
        return data;
    }
    
    @Override
    public Map<String, Long> groupForDevice() {
        Query query = entityManager.createQuery("SELECT mar.device, COUNT(mar) FROM MemberActiveRecords mar GROUP BY mar.device");
        @SuppressWarnings("unchecked")
        List<Object[]> resultList = query.getResultList();
        return toGroupResultMap(resultList);
    }
    
    @Override
    public Map<String, Long> groupForISP() {
        Query query = entityManager.createQuery("SELECT mar.isp, COUNT(mar) FROM MemberActiveRecords mar GROUP BY mar.isp");
        @SuppressWarnings("unchecked")
        List<Object[]> resultList = query.getResultList();
        return toGroupResultMap(resultList);
    }
    
    @Override
    public Map<String, Long> groupForProvince() {
        Query query = entityManager.createQuery("SELECT mar.province, COUNT(mar) FROM MemberActiveRecords mar GROUP BY mar.province");
        @SuppressWarnings("unchecked")
        List<Object[]> resultList = query.getResultList();
        return toGroupResultMap(resultList);
    }
    
    @Override
    public TreeMap<String, Long> groupMemberForActivity(LocalDateTime start, LocalDateTime finish) {
        final String SQL = "SELECT FUNCTION('DATE', mar.activeDateTime), COUNT(mar) FROM MemberActiveRecords mar WHERE mar.activeDateTime BETWEEN ?1 AND ?2 GROUP BY FUNCTION('DATE', mar.activeDateTime)";
        @SuppressWarnings("unchecked")
        List<Object[]> result = entityManager.createQuery(SQL).setParameter(1, start).setParameter(2, finish).getResultList();
        //
        TreeMap<String, Long> data = new TreeMap<>();
        for (Object[] columns : result) {
            String k = null;
            try {
                k = (String) columns[0];
            } catch (java.lang.ClassCastException e) {
                k = columns[0].toString();
            }
            Long v = 0L;
            try {
                v = ((BigDecimal) columns[1]).longValue();
            } catch (java.lang.ClassCastException e) {
                v = (Long) columns[1];
            }
            if (k == null || v == null) {
                continue;
            }
            data.put(k, v);
        }
        return data;
    }
    
    @Override
    public Page<MemberActiveRecords> findAll(Pageable pageable) {
        final long total = count();
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<MemberActiveRecords> query = entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar ORDER BY mar.activeDateTime DESC", MemberActiveRecords.class);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<MemberActiveRecords> result = query.getResultStream();
        return new Page<MemberActiveRecords>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<MemberActiveRecords> getResult() {
                return result;
            }
        };
    }
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void save(MemberActiveRecords entity) {
        entityManager.persist(entity);
    }
    
    @Override
    public Optional<MemberActiveRecords> findOne(Long primaryKey) {
        MemberActiveRecords mar = entityManager.find(MemberActiveRecords.class, primaryKey);
        return Optional.ofNullable(mar);
    }
    
    @Override
    public Optional<Boolean> edit(MemberActiveRecords updateEntity) {
        return Optional.empty();
    }
    
    @Override
    public Stream<MemberActiveRecords> findAll() {
        return Stream.empty();
    }
    
    @Override
    public long count() {
        try {
            return entityManager.createQuery("SELECT COUNT(mar) FROM MemberActiveRecords mar", Long.class).getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[count][MemberActiveRecordsDao]", e);
            }
        }
        return 0L;
    }
    private Map<String, Long> toGroupResultMap(List<Object[]> result) {
        Map<String, Long> data = new HashMap<>();
        if (result == null || result.isEmpty()) {
            return data;
        }
        for (Object[] objArr : result) {
            String attrVal = null;
            if (objArr[0] instanceof String) {
                attrVal = objArr[0].toString();
            }
            if (attrVal == null) {
                continue;
            }
            Long size = 0L;
            try {
                size = ((BigDecimal) objArr[1]).longValue();
            } catch (ClassCastException e) {
                size = (Long) objArr[1];
            }
            data.put(attrVal, size);
        }
        return data;
    }
    private Map<ForumActionEnum, Long> groupMemberActionResultMapping(List<Object[]> result) {
        Map<ForumActionEnum, Long> data = new HashMap<>();
        if (result == null || result.isEmpty()) {
            return data;
        }
        for (Object[] objArr : result) {
            ForumActionEnum action = null;
            if (objArr[0] instanceof ForumActionEnum) {
                action = (ForumActionEnum) objArr[0];
            }
            if (action == null) {
                continue;
            }
            Long size = 0L;
            try {
                size = ((BigDecimal) objArr[1]).longValue();
            } catch (ClassCastException e) {
                size = (Long) objArr[1];
            }
            data.put(action, size);
        }
        return data;
    }
}